// ----------------------------------------
// Filename: mqueue.c
// Description: The code that implements a ready queue
// Author: Jon Maloney
// Date: 30-May-2012
//

//************************************************************
//                    Include .h files
//************************************************************
#include "mqueue.h"
#include <stdlib.h>
//************************************************************
//                    Include external references
//************************************************************

//************************************************************
//                    Define Statements
//************************************************************

//************************************************************
//                    Struct, Union, enum Statements
//************************************************************

//************************************************************
//                    Global variables and constants
//************************************************************

//************************************************************
//                    Prototype Private Functions
//************************************************************

//************************************************************
//                    Implementation
//************************************************************

// ----------------------------------------
// mqueue_Create
// 
// Initialize the queue
// Input:
//   NbItem: 	the maximum number of items in the queue
//	 ItemSize:	the size of each item in the queue.
// Output:
//   m_QueueHandle a handle to the queue that has been created.
//		If null the queue could not be created.
// Conditions:
//  none
m_QueueHandle mqueue_Create(UINT8 NbItem, UINT8 ItemSize)
{
	queue_t * mQueue;
	
	//Allocates some heap memory to the queue structure
	mQueue  = (queue_t *)malloc(sizeof(queue_t));

	//If malloc failed return NULL
	if(mQueue == NULL)
		return NULL;

	//Allocates some heap memory to the queue buffer
	mQueue->Buffer = (ptr_8)malloc(NbItem * ItemSize);
	
	//If malloc failed return NULL
	if(mQueue->Buffer == NULL)
		return NULL;
	
	//Create a binary semaphore to be used by the queue
	mQueue->q_Semaphore = (bsem_t *)sem_Create( pdFALSE );
	
	mQueue->Head = 0;
	mQueue->Tail = 0;
	mQueue->NbItem = NbItem;
	mQueue->ItemSize = ItemSize;
	mQueue-> ItemNb = 0;

	return (m_QueueHandle)mQueue;
}

// ----------------------------------------
// mqueue_Delete
// 
// Deletes the queue
// Input:
//   mQueue 		is a queue handle (a pointer to a queue structure)
// Output:
//   none
// Conditions:
//  none
void mqueue_Delete(m_QueueHandle mQueue)
{
	sem_Delete(mQueue->q_Semaphore);
	
	mQueue->Head = 0;
	mQueue->Tail = 0;
	mQueue->NbItem = 0;
	mQueue->ItemSize = 0;
	mQueue-> ItemNb = 0;
	
	free(mQueue->Buffer);
	
	mQueue->Buffer = 0;

	free(mQueue);
	mQueue = NULL;

}

// ----------------------------------------
// mqueue_Put
// 
// Add an item to the next space available in the queue
// Input:
//   mQueue 		is a queue handle (a pointer to a queue structure)
//   ItemPtr: 		is the data to store in the queue
//	 delay:			The maximum time that the queue will wait for data to become available (0 = wait forever)
// Output:
//   TRUE if data is properly added to the queue
//	 FALSE if data could not be placed in queue
//		Either queue is full, 
//		There is no room on the heap to place the item,
//		queue handle is NULL.
// Conditions:
//   
BOOL mqueue_Put(m_QueueHandle mQueue, void * ItemPtr, kernelTickType delay)
{
	UINT8 x;
	
	//Check to make sure the queue handle is not NULL
	if(mQueue == NULL)
		return (pdFALSE);
	
	ENTER_CRITICAL();
  
	//Check to see if the queue is full.
	if (mQueue->ItemNb == mQueue->NbItem-1) 
	{
		EXIT_CRITICAL();
		return pdFALSE;
	}
	
	//Copy the item into the queue
	for(x = 0; x < mQueue->ItemSize; x++)
	{
		*(mQueue->Buffer + mQueue->Head + x) = *(((UINT8 *)ItemPtr) + x);
	}
	
	//Move the position of the head of the queue
	mQueue->Head+=mQueue->ItemSize;
	
	//Wrap the head around the circular buffer
	if(mQueue->Head >= mQueue->ItemSize*mQueue->NbItem)
		mQueue->Head = 0;

	//Increment the count of items in the queue.
	mQueue->ItemNb++;
	
	EXIT_CRITICAL();

	//Signal the consumer that there is a value in the queue
	Semaphore_Signal(mQueue->q_Semaphore);
	
	return (pdTRUE);
} 

// ----------------------------------------
// mqueue_Get
// 
// Remove the next item from the queue
// Input:
//   mQueue: 	is a queue handle (a pointer to a queue structure)
//	 ItemPtr:	a pointer to the location that the next item in the queue will be placed.
//	 delay:		The maximum time that the queue will wait for data to become available (0 = wait forever)
// Output:
//   TRUE if the operation was successful and the data is valid
//	 False if the get operation failed.
//		either the queue is empty or
//		the queue handle is NULL
// Conditions:
//   none

BOOL mqueue_Get(m_QueueHandle mQueue, void * ItemPtr, kernelTickType delay)
{	
	UINT8 x;
	
	//Check to make sure the queue handle is not NULL
	if(mQueue == NULL)
		return pdFALSE;

	ENTER_CRITICAL();
	//only wait on the semaphore if the queue is empty
	if (mQueue->ItemNb == 0)
	{
		EXIT_CRITICAL();
		if(Semaphore_Wait(mQueue->q_Semaphore, delay) == pdFALSE)
			return pdFALSE;
	}

	ENTER_CRITICAL();
	
	//Check to see if the queue is empty.
	if (mQueue->ItemNb == 0) 
	{
		EXIT_CRITICAL();
		return pdFALSE;
	}
	
	//Copy the item from the queue
	for(x = 0; x < mQueue->ItemSize; x++)
	{
		*(((UINT8*)ItemPtr) + x) = *(mQueue->Buffer + mQueue->Tail + x);
	}
	
	//Move the position of the tail of the queue
	mQueue->Tail+=mQueue->ItemSize;

	//Wrap the tail around the circular buffer
	if(mQueue->Tail >= mQueue->ItemSize*mQueue->NbItem)
		mQueue->Tail = 0;
	
	//decrement the count of items in the queue.
	mQueue->ItemNb--;
	
	EXIT_CRITICAL();
	return pdTRUE;
}

// ----------------------------------------
// mqueue_IsSpaceAvailable
// 
// Determines if there is room left in the queue
//	
// Input:
//  mQueue is a queue handle (a pointer to a queue structure)
// Output:
// 	TRUE if there is room left in the queue
//	FALSE if the queue is full or
//		  if the queue handle is NULL
// Conditions:
//   none
inline BOOL mqueue_IsSpaceAvailable(m_QueueHandle mQueue)
{
	//Check to see if the queue is full.
	if (mQueue->ItemNb == mQueue->NbItem-1) 
		return pdTRUE;
	else
		return pdFALSE;
}

// ----------------------------------------
// mqueue_IsEmpty
// 
// Determines if the queue is empty
// Input:
//  mQueue is a queue handle (a pointer to a queue structure)
// Output:
// 	TRUE if the queue is empty
//	FALSE if the queue is not empty
//		  if the queue handle is NULL
// Conditions:
//   none
inline BOOL mqueue_IsEmpty(m_QueueHandle mQueue)
{
	//Check to see if the queue is empty.
	if (mQueue->ItemNb == 0) 
		return pdTRUE;
	else
		return pdFALSE;
}
